<script setup lang="ts">
import DocumentFile from "./document-file.vue";
import mittBus from "../../global/bus/mittBus.ts";
import {
  addModelApi,
  createModelApi,
  deleteModelApi,
  editModelApi,
  getModelAllApi,
} from "../../api/modelApi.ts";
import { ComponentInternalInstance, getCurrentInstance, ref, watch } from "vue";
import { useMouse, useEventListener, useStorage } from "@vueuse/core";
import { getUserId } from "../../cache/userStorage.ts";
import MyDialog from "../dialog/my-dialog.vue";
import MyInput from "../input/my-input.vue";
import { postDownloadFileFromFileServer } from "../../api/http.ts";
import draggable from "vuedraggable";
import { ElNotification } from "element-plus";
import UploadDocumentDialog from "../dialog/upload-document-dialog.vue";
import {
  createEmptyFiles,
  createFileWithContent,
} from "../../util/fileUtils.ts";
import { uploadFile, replaceFile } from "../../api/fileUploadApi.ts";
import FileUploadRes from "../../model/FileUploadRes.ts";
import { FormModel } from "../../types/FormModel.ts";
import txt from "./txt.vue";
import { goMarkdown, getPdf, goWord } from "../../util/routerUtils.ts";

const { proxy } = getCurrentInstance() as ComponentInternalInstance;

const parentId = ref();
watch(
  () => parentId.value,
  (newId) => {
    parentIdState.value = newId;
  }
);
const parentIdState = useStorage("fileParentId", 0); // returns Ref<number>
if (parentIdState.value) {
  parentIdState.value = 0;
}

const uploadDocumentShow = ref(false);
const uploadDocumentDialogRef = ref<any>(null);
const hideUploadClick = () => {
  uploadDocumentShow.value = false;
};
const sureUploadClick = () => {
  uploadDocumentShow.value = false;
};

const uploadFileInFolder = async (file: File) => {
  const res = (await uploadFile(file)) as FileUploadRes;
  const value: any = {
    isFolder: false,
    url: res.url,
    size: res.size,
    name: file.name,
    mine: res.mine,
    ownerId: getUserId(),
  };
  if (parentId.value) {
    value.parentId = parentId.value;
  }
  await addModelApi(value, "document.file");
};

// 文件列表数据
const fileList = ref<any[]>([]);

const getAllFileByParentId = () => {
  let condition = "";
  if (parentId.value) {
    condition = `('parentId',=,${parentId.value})`;
  }
  getModelAllApi(
    "id,name,isFolder,url,size,mine,ownerId",
    condition,
    "document.file"
  ).then((data) => {
    fileList.value.splice(0, fileList.value.length);
    fileList.value.push(...data);
  });
};

/**
 * @author lwlianghehe@gmail.com
 * @date 2025/04/05 16:36
 */
// 定义props和emits
const props = defineProps<{
  modelValue: any[];
  formModel?: FormModel;
}>();

const emit = defineEmits<{
  "update:modelValue": [value: any[]];
}>();

// 监听外部传入的数据变化
watch(
  () => props.modelValue,
  (newValue) => {
    if (newValue) {
      fileList.value = [...newValue];
    }
  },
  { immediate: true }
);

// 监听内部数据变化，向外部发送更新
watch(
  fileList,
  (newValue) => {
    emit("update:modelValue", newValue);
  },
  { deep: true }
);

mittBus.on("uploadFile", async () => {
  console.log("upload-file");
  if (!parentId.value) {
    const defaultValue = await createModelApi({}, "document.show.transient");
    fileList.value.splice(0, fileList.value.length);
    fileList.value.push(...defaultValue.documents);
  } else {
    getAllFileByParentId();
  }
});

const createFolderShow = ref(false); // 创建文件夹
const { x, y } = useMouse();

const menuPosition = ref({ x: 0, y: 0 });

const showListMenu = () => {
  console.log("showListMenu true");
  fileSelectedIndex.value = 0;
  fileSelectedTag.value = null;
  createFolderShow.value = true;
  createFileShow.value = false;
  openFolderShow.value = false;
  menuPosition.value.x = x.value - 90;
  menuPosition.value.y = y.value - 90;
};

useEventListener("click", () => {
  console.log("click true");
  createFolderShow.value = false;
  createFileShow.value = false;
  openFolderShow.value = false;
  fileSelectedIndex.value = 0;
  //fileSelectedTag.value = null
});

const createFolderClick = async () => {
  createFolderNameShow.value = true;
};
const createTextFileClick = () => {
  createTextFileShow.value = true;
};
const createTextFileShow = ref(false);
const hideTextFileClick = () => {
  createTextFileShow.value = false;
};
const sureTextFileClick = async () => {
  const file = createEmptyFiles.text(textFileName.value);
  uploadFileInFolder(file).then(() => {
    // mittBus.emit('uploadFile')
    createTextFileShow.value = false;
  });
};
const textFileName = ref("");

const createFolderNameShow = ref(false);
const hideClick = () => {
  createFolderNameShow.value = false;
};
const sureClick = async () => {
  if (!folderName.value || !folderName.value.toString().trim()) {
    ElNotification({
      title: "提示",
      message: "输入文件夹名称",
      type: "error",
    });
    return;
  }
  const value: any = {
    isFolder: true,
    name: folderName.value,
    ownerId: getUserId(),
  };
  if (parentId.value) {
    value.parentId = parentId.value;
  }
  await addModelApi(value, "document.file");
  createFolderNameShow.value = false;
  folderName.value = "";
  mittBus.emit("uploadFile");
};
const folderName = ref("");

const createFileShow = ref(false); // 下载文件

const downloadFileClick = async () => {
  const fileNameTemp = fileSelectedTag.value.name;
  postDownloadFileFromFileServer(fileSelectedTag.value.url, {}).then(
    (response) => {
      // 创建一个 URL 对象
      const url = window.URL.createObjectURL(new Blob([response.data]));

      // 创建一个临时的下载链接
      const link = document.createElement("a");
      link.href = url;
      // 获取 Content-Disposition 响应头
      const contentDisposition = response.headers.get("Content-Disposition");

      // 提取文件名
      let fileName = fileNameTemp;
      if (contentDisposition && contentDisposition.includes("filename=")) {
        const fileNameMatch = contentDisposition.match(/filename="?(.+)"?/);
        if (fileNameMatch.length > 1) {
          fileName = decodeURIComponent(fileNameMatch[1]);
        }
      }
      // 文件名设置为导出的文件名
      link.setAttribute("download", fileName);

      // 触发下载
      document.body.appendChild(link);
      link.click();

      // 清理 URL 对象
      document.body.removeChild(link);
      window.URL.revokeObjectURL(url);
      ElNotification({
        title: "提示",
        message: "下载完成",
        type: "success",
      });
    }
  );
};

const deleteFileClick = async () => {
  deleteModelApi(fileSelectedTag.value.id, "document.file").then(() => {
    mittBus.emit("uploadFile");
  });
};

const deleteFolderClick = async () => {
  deleteModelApi(fileSelectedTag.value.id, "document.file").then(() => {
    mittBus.emit("uploadFile");
  });
};

const fileSelectedIndex = ref(0);
const fileSelectedTag = ref<any>(null);
const showFileMenu = (id: number) => {
  createFolderShow.value = false;
  const index = fileList.value.findIndex((x: any) => x.id == id);
  fileSelectedIndex.value = index;
  menuPosition.value.x = x.value - 90;
  menuPosition.value.y = y.value - 90;
  fileSelectedTag.value = fileList.value[index];
  if (fileSelectedTag.value.isFolder) {
    openFolderShow.value = true;
    createFileShow.value = false;
  } else {
    createFileShow.value = true;
    openFolderShow.value = false;
  }
};

const openFolderShow = ref(false); // 右击文件夹时打开
const openFolderClick = () => {
  openFolderShow.value = false;
  parentId.value = fileSelectedTag.value.id;
  getAllFileByParentId();
};

const openFileClick = (id: number) => {
  const doc = fileList.value.find((x: any) => x.id == id);
  if (doc && doc.isFolder) {
    parentId.value = doc.id;
    getAllFileByParentId();
  }
};

const activeFileId = ref(0);
const activeFileClick = (id: number) => {
  activeFileId.value = id;
};

const uploadFileClick = () => {
  uploadDocumentShow.value = true;
  uploadDocumentDialogRef.value.uploadFileClick();
};
const uploadFolderClick = () => {
  uploadDocumentShow.value = true;
  uploadDocumentDialogRef.value.uploadDirClick();
};

const dragging = ref(false);

const onDragEnd = (evt: any) => {
  dragging.value = false;
  if (!evt.to) return; // 未移动

  if (!dragIntoItem.value) {
    return;
  }
  const src = evt.item.__draggable_context.element;

  // 调整目录
  let newSrc = { id: src.id, parentId: dragIntoItem.value };

  editModelApi(newSrc, "document.file").then(() => {
    const index = fileList.value.findIndex((x: any) => x.id == src.id);
    fileList.value.splice(index, 1);
  });
};
const onDragStart = (evt: any) => {
  dragging.value = true;
  const currentElement = evt.item.__draggable_context.element;
  if (currentElement.id != activeFileId.value) {
    activeFileId.value = currentElement.id;
  }
};
const dragIntoItem = ref(0);

const ondragenter = (element: any) => {
  if (element && element.isFolder) {
    dragIntoItem.value = element.id;
  }
};
const onDragOver = (element: any) => {
  const target = element;
  if (target && target.isFolder) {
    dragIntoItem.value = target.id;
  }
};
const ondragleave = (element: any) => {
  dragIntoItem.value = 0;
};

const editFileShow = ref(false);
const hideEditFileClick = () => {
  editFileShow.value = false;
};
const sureEditFileClick = () => {
  const value: any = {
    id: fileSelectedTag.value.id,
    name: editFileName.value,
  };
  editModelApi(value, "document.file").then(() => {});
  editFileShow.value = false;
  editFileName.value = "";
};
const editFileClick = () => {
  editFileShow.value = true;
};
const editFileName = ref("");

// 修改文本文件
const txtShow = ref(false);
const txtFileName = ref<any>(null);
const txtFileContent = ref<any>(null);
const editTxtFileClick = () => {
  txtFileName.value = fileSelectedTag.value.name;
  if (
    txtFileName.value.endsWith(".txt") ||
    txtFileName.value.endsWith(".json") ||
    txtFileName.value.endsWith(".xml") ||
    txtFileName.value.endsWith(".html") ||
    txtFileName.value.endsWith(".css") ||
    txtFileName.value.endsWith(".js") ||
    txtFileName.value.endsWith(".ts") ||
    txtFileName.value.endsWith(".java") ||
    txtFileName.value.endsWith(".py")
  ) {
    postDownloadFileFromFileServer(fileSelectedTag.value.url, {}).then(
      (response) => {
        txtFileName.value = fileSelectedTag.value.name;
        response.data.text().then((text: string) => {
          txtFileContent.value = text;
        });
      }
    );
    txtShow.value = true;
  } else if (txtFileName.value.endsWith(".md")) {
    goMarkdown(fileSelectedTag.value.id);
  } else if (txtFileName.value.endsWith(".pdf")) {
    getPdf(fileSelectedTag.value.url);
  } else if (txtFileName.value.endsWith(".docx")) {
    goWord(fileSelectedTag.value.id);
  } else {
    ElNotification({
      title: "提示",
      message: "不支持的文件类型，请下载后查看",
      type: "warning",
    });
  }
};
const hideTxtClick = () => {
  txtShow.value = false;
};
const saveTxtClick = (content: string, fileName: string) => {
  const file = createFileWithContent(fileName, content);
  const url = fileSelectedTag.value.url.replace("/file/down", "");
  replaceFile(url, file).then(() => {
    const value = {
      id: fileSelectedTag.value.id,
      size: file.size,
    };
    editModelApi(value, "document.file").then(() => {
      txtShow.value = false;
    });
  });
};
</script>

<template>
  <div class="min-h-full" @contextmenu.prevent.stop="showListMenu">
    <el-scrollbar>
      <draggable
        :list="fileList"
        class="flex items-start flex-wrap gap-2 min-h-full"
        item-key="id"
        group="files"
        :sort="false"
        @start="onDragStart"
        @end="onDragEnd"
      >
        <template #item="{ element }">
          <div
            :class="[
              'inline-block hover:bg-gray-200 p-2 rounded-lg',
              {
                'bg-gray-200':
                  activeFileId == element.id || dragIntoItem == element.id,
              },
            ]"
            @click="activeFileClick(element.id)"
            @dblclick="openFileClick(element.id)"
            @dragover="onDragOver(element)"
            @dragleave="ondragleave(element)"
            @dragenter="ondragenter(element)"
            @contextmenu.prevent.stop="showFileMenu(element.id)"
          >
            <document-file :file="element" />
          </div>
        </template>
      </draggable>
      <div v-if="!fileList.length">暂无文件</div>
    </el-scrollbar>
    <div
      v-if="createFolderShow"
      class="context-menu"
      :style="{ top: `${menuPosition.y}px`, left: `${menuPosition.x}px` }"
    >
      <div class="px-2 py-1 cursor-pointer" @click="createFolderClick">
        创建文件夹
      </div>
      <div class="px-2 py-1 cursor-pointer" @click="createTextFileClick">
        创建文本文件
      </div>
      <div class="px-2 py-1 cursor-pointer" @click="uploadFileClick">
        上传文件
      </div>
      <div class="px-2 py-1 cursor-pointer" @click="uploadFolderClick">
        上传文件夹
      </div>
    </div>
    <div
      v-if="createFileShow"
      class="context-menu"
      :style="{ top: `${menuPosition.y}px`, left: `${menuPosition.x}px` }"
    >
      <div class="px-2 py-1 cursor-pointer" @click="editFileClick">
        修改文件名
      </div>
      <div class="px-2 py-1 cursor-pointer" @click="editTxtFileClick">
        修改文件
      </div>
      <div class="px-2 py-1 cursor-pointer" @click="downloadFileClick">
        下载
      </div>
      <div class="px-2 py-1 cursor-pointer" @click="deleteFileClick">删除</div>
    </div>
    <div
      v-if="openFolderShow"
      class="context-menu"
      :style="{ top: `${menuPosition.y}px`, left: `${menuPosition.x}px` }"
    >
      <div class="px-2 py-1 cursor-pointer" @click="deleteFolderClick">
        删除
      </div>
    </div>
  </div>
  <MyDialog
    v-model="createFolderNameShow"
    @close="hideClick"
    @sure="sureClick"
    title="输入文件夹名称"
  >
    <MyInput ref="db_input" v-model="folderName" :required="true"></MyInput>
  </MyDialog>
  <MyDialog
    v-model="createTextFileShow"
    width="400"
    @close="hideTextFileClick"
    @sure="sureTextFileClick"
    title="输入文件名称"
  >
    <MyInput
      ref="db_text_input"
      v-model="textFileName"
      :required="true"
    ></MyInput>
  </MyDialog>
  <MyDialog
    v-model="editFileShow"
    width="400"
    @close="hideEditFileClick"
    @sure="sureEditFileClick"
    title="修改文件名称"
  >
    <MyInput
      ref="db_edit_input"
      v-model="editFileName"
      :required="true"
    ></MyInput>
  </MyDialog>

  <upload-document-dialog
    ref="uploadDocumentDialogRef"
    v-model="uploadDocumentShow"
    title="上传文件"
    @close="hideUploadClick"
    @sure="sureUploadClick"
  >
  </upload-document-dialog>

  <el-dialog
    :close-on-click-modal="false"
    :draggable="true"
    v-model="txtShow"
    width="900"
    @close="hideTxtClick"
    title="修改文件"
  >
    <div class="h-[600px]">
      <txt
        class="w-full"
        @save="saveTxtClick"
        :initContent="txtFileContent"
        :fileName="txtFileName"
      ></txt>
    </div>
  </el-dialog>
</template>

<style scoped>
.context-menu {
  position: absolute;
  background-color: #fff;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  z-index: 1000;
  width: 150px;
}
</style>
